Android 屏幕适配的理解

作为一名Android Developer,有一个“顽疾”一直都无法绕开,那就是屏幕适配,毕竟Android自由的同时,也引入了很多“奇奇怪怪”的东西,比如各种各样的屏幕大小/分辨率等等,也就是导致了作为应用开发者,必须思考如何让自己的应用正常适配更多的机型。本文主要介绍一些关于屏幕显示的基本概念,以及目前主流的屏幕适配思想。

基础知识

获取DisplayMetrics

1
2
3
4
5
//Display屏幕显示的详细信息
Display display = getWindowManager().getDefaultDisplay();
//从Display获取DisplayMetrics信息
DisplayMetrics metrics = new DisplayMetrics();
display.getMetrics(metrics);

修改DisplayMetrics

上面获取的DisplayMetrics只可读,如下是在APP中修改displayMetrics来动态适配的。

1
2
3
4
5
6
7
8
9
10
 //修改屏幕density
DisplayMetrics displayMetrics = getResources().getDisplayMetrics();
if(displayMetrics.widthPixels == 1024
&& displayMetrics.heightPixels == 600
&& displayMetrics.densityDpi == DisplayMetrics.DENSITY_HIGH) {
Configuration cfg = getResources().getConfiguration();
displayMetrics.densityDpi = DisplayMetrics.DENSITY_MEDIUM;
cfg.densityDpi = DisplayMetrics.DENSITY_MEDIUM;
getResources().updateConfiguration(cfg, displayMetrics);
}

通过以上代码,基本上对图片和字体都能完美适配了,但是对于代码创建的bitmap还是会有问题,bitmap创建时默认会使用系统的density, 我们需要创建的时候指定我们修改后的displayMetrics

1
Bitmap bitmap = Bitmap.createBitmap(displayMetrics,width,height,config);

或者在创建完成后手动调用bitmap的setDensity方法修改density

1
bitmap.setDensity(DisplayMetrics.DENSITY_MEDIUM);

DisplayMetrics信息

  • widthPixels:屏幕宽度;

  • heightPixels:屏幕高度;

  • densityDpi:屏幕密度,每英寸的像素数;

  • density:密度比值,其实就是densityDpi/160得出来的值(160密度就是谷歌定的一个密度标准),不同手机dp换算px就是通过该值;

  • scaledDensity:同density,用于文字缩放的计算,也就是sp;

  • xDpi:水平方向的真实密度;

  • yDpi:垂直方向的真是密度;

适配方案

对于适配方案,目前主要有两种思想:

  • 对于不同分辨率,可以做到相同的缩放比,即显示内容一样,比如下图:

    image-20200617103349066

  • 对于不同的屏幕,屏幕越大显示更多的内容,如下图:

    image-20200617103318144

两种方案没有优劣,只有适合的业务场景!!!下面我们重点提一下第一种思路,介于这个问题在伴随着Android成长的过程就开始逐渐被思考解决,方案基本成熟,所以选择了一种使用比较广泛的给大家科普一下,也方便自己记忆。

AndroidAutoSize

JessYanCoding根据之前今日头条屏幕适配方案的改进版,是一个非常优秀的、极低成本的 Android 屏幕适配方案,属于我们上面提到的前者,具体的内容详见github

基本原理:
1
当前设备屏幕总宽度(单位为像素)/ 设计图总宽度(单位为 dp) = density

density 的意思就是 1 dp 占当前设备多少像素,

核心点就是将设计图的dp转化成根据不同屏幕的density计算得到的px值,关于这个公式的计算,是在appAndroidManifest.xml 填写设计图的宽高dp值,然后声明一个 ContentProvider,在它的 onCreate 方法中启动框架即可,在 App 启动时,系统会在 App 的主进程中自动实例化你声明的这个 ContentProvider,并调用它的 onCreate 方法,执行时机比 Application#onCreate 还靠前,可以做一些初始化的计算工作,比如动态的设置DisplayMetrics的density值。这样其实在开发过程中,你只需要按照设计图中规定的dp值,设定相应的控件即可,

参考